// Updated by Leng Bo, 2008-07-05
// This is only for test in loader.
// Please add more code such as fly point handling for touch.

#include <arch.h>
#include <t8.h>
#include <util.h>
#include <regs-adc.h>

#define USING_CALIBRATION

#define WAIT4INT(x)  (((x)<<8) | \
             S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
             S3C2410_ADCTSC_XY_PST(3))

#define AUTOPST         (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
             S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))

void GetDisplayPoint(int x, int y, int *pX, int *pY);

int state = 0; // 0: Up 1: Down
int prev_x = 0, prev_y = 0;

void TouchInit()
{
    rCLKCON |= 1 << 15; // Enable the clock of touch screen
    ostdelay(1000);

    rADCCON = (S3C2410_ADCCON_PRSCEN) | S3C2410_ADCCON_PRSCVL(9); // Prescaler enabled
    rADCDLY = 50000;     //Normal conversion mode delay about (1/3.6864M)*50000=13.56ms

    rADCTSC = WAIT4INT(0);
//    rADCCON |= S3C2410_ADCCON_ENABLE_START; //start ADC
//    while(rADCCON & S3C2410_ADCCON_ENABLE_START); //check if Enable_start is low
//    //check if EC(End of Conversion) flag is high, This line is necessary~!!
//    while(!(rADCCON & S3C2410_ADCCON_ECFLG));
}

static void GetTouchDataFromRegs(TOUCH_DATA *pData)
{
    unsigned short adcdat0, adcdat1;

    rADCTSC = S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST;
    rADCDLY = 40000;
    rADCCON |= S3C2410_ADCCON_ENABLE_START; //start ADC
    while(rADCCON & S3C2410_ADCCON_ENABLE_START); //check if Enable_start is low
    //check if EC(End of Conversion) flag is high, This line is necessary~!!
    while(!(rADCCON & S3C2410_ADCCON_ECFLG));

    adcdat0 = rADCDAT0;
    adcdat1 = rADCDAT1;

    // printk("dump %x %x\n", adcdat0, adcdat1);

    pData->x = adcdat0 & S3C2410_ADCDAT0_XPDATA_MASK;
    pData->y = adcdat1 & S3C2410_ADCDAT1_YPDATA_MASK;
    // 0: UP, 1: Down
    pData->dir = (!(adcdat0 & S3C2410_ADCDAT0_UPDOWN)) && (!(adcdat1 & S3C2410_ADCDAT0_UPDOWN));

#ifdef USING_CALIBRATION
    printk("Raw coordinates: (%d, %d)\n", pData->x, pData->y);
    GetDisplayPoint(pData->x, pData->y, &pData->x, &pData->y);
#endif
}

// Only test code. Polling mode with interrupt bit check.
int TouchRead(TOUCH_DATA *pData)
{
    int interrupt = rSUBSRCPND & (1 << 9);
    int ret;

    if (interrupt) {
        if (state == 0) {
            GetTouchDataFromRegs(pData);
            if (pData->dir != 1) {
                printk("Maybe something wrong with touch direction."\
                       "It should be 1, actually it is \n", pData->dir);
                pData->dir = 1;
            }
            rADCDLY = 50000;
            rADCTSC = WAIT4INT(1);

            state = 1;
            ret = 0;
        }
        else {
#if 0
            GetTouchDataFromRegs(pData);
            if (pData->dir != 0) {
                printk("Maybe something wrong with touch direction."\
                       "It should be 0, actually it is \n", pData->dir);
                pData->dir = 0;
            }
#endif

            pData->x = prev_x;
            pData->y = prev_y;
            pData->dir = 0;

            rADCTSC = WAIT4INT(0);
            rADCDLY = 50000;

            state = 0;
            ret = 0;
        }
        rSUBSRCPND |= (1 << 9); // Clear interrupt bit
    }
    else if (state == 1) {
        // Move
        GetTouchDataFromRegs(pData);
        pData->dir = 2;
        rADCTSC = WAIT4INT(1);
        ret = 0;
    }
    else {
        // No touch
        ret = -1;
    }

    prev_x = pData->x;
    prev_y = pData->y;

    return ret;
}

////////////////////////////////////////////////////////////////////////////////
// Calibration code
//
typedef struct {
    int x;//Raw Coordinate
    int y;//Raw
    int X;//LCD Point
    int Y;
}RefPoint;

typedef struct {
    int x1, y1;
    int X1, Y1;
    int x2, y2;
    int X2, Y2;
}CalibrationInfo;

#define x1  g_Cali.x1
#define y1  g_Cali.y1
#define X1  g_Cali.X1
#define Y1  g_Cali.Y1
#define x2  g_Cali.x2
#define y2  g_Cali.y2
#define X2  g_Cali.X2
#define Y2  g_Cali.Y2

extern CalibrationInfo g_Cali;

//X = Ax + (By) + C
//Y = (Dx) + Ey + F

CalibrationInfo g_Cali = {156, 884, 10, 10, 800, 156, 200, 300};


void GetDisplayPoint(int x, int y, int *pX, int *pY)
{
    *pX = X1 + (x - x1) * (X2 - X1) / (x2 - x1);
    *pY = Y1 + (y - y1) * (Y2 - Y1) / (y2 - y1);

// This file is only for test, so we needn't do the corrections below.
#if 0
    if (*pX < 0) *pX = 0;
    if (*pX > LCD_WIDTH - 1) *pX = LCD_WIDTH - 1;
    if (*py < 0) *py = 0;
    if (*py > LCD_HEIGHT - 1) *py = LCD_HEIGHT - 1;
#endif
}
